As you have seen, a key element of the memory-management scheme presented in this chapter is to disallow any nonessential memory allocation requests that would deplete the memory cushion. In practice, this means that, before calling NewHandle , NewPtr , or another function that allocates memory, you should check that the amount of space remaining after the allocation, if successful, exceeds the size of the memory cushion.
An easy way to do this is never to allocate memory for nonessential tasks by calling NewHandle or NewPtr directly. Instead call a function such as NewHandleCushion , defined in Listing 1-6 , or NewPtrCushion , defined in Listing 1-7 .
Listing 6 Allocating relocatable blocks
FUNCTION NewHandleCushion (logicalSize: Size): Handle;
BEGIN
IF NOT IsMemoryAvailable(logicalSize) THEN
NewHandleCushion := NIL
ELSE
BEGIN
SetGrowZone(NIL); {remove grow-zone function}
NewHandleCushion := NewHandleClear(logicalSize);
SetGrowZone(@MyGrowZone); {install grow-zone function}
END;
END;
The NewHandleCushion function first calls IsMemoryAvailable to determine whether allocating the requested number of bytes would deplete the memory cushion. If so, NewHandleCushion returns NIL to indicate that the request has failed. Otherwise, if there is indeed sufficient space for the new block, NewHandleCushion calls NewHandleClear to allocate the relocatable block. Before calling NewHandleClear , however, NewHandleCushion disables the grow-zone function for the application heap. This prevents the grow-zone function from releasing any emergency memory reserve your application might be maintaining. See "Defining a Grow-Zone Function" for details on grow-zone functions.
You can define a function NewPtrCushion to handle allocation of nonrelocatable blocks, as shown in Listing 1-7 .
Listing 7 Allocating nonrelocatable blocks
FUNCTION NewPtrCushion (logicalSize: Size): Handle;
BEGIN
IF NOT IsMemoryAvailable(logicalSize) THEN
NewPtrCushion := NIL
ELSE
BEGIN
SetGrowZone(NIL); {remove grow-zone function}
NewPtrCushion := NewPtrClear(logicalSize);
SetGrowZone(@MyGrowZone); {install grow-zone function}
END;
END;
The functions NewHandleCushion and NewPtrCushion allocate prezeroed blocks in your application heap. You can easily modify those functions if you do not want the blocks prezeroed.
Listing 1-8 illustrates a typical way to call NewPtrCushion .
Listing 8 Allocating a dialog record
FUNCTION GetDialog (dialogID: Integer): DialogPtr;
VAR
myPtr: Ptr; {storage for the dialog record}
BEGIN
myPtr := NewPtrCushion(SizeOf(DialogRecord));
IF MemError = noErr THEN
GetDialog := GetNewDialog(dialogID, myPtr, WindowPtr(-1))
ELSE
GetDialog := NIL; {can't get memory}
END;
When you allocate memory directly, you can later release it by calling the DisposeHandle and DisposePtr procedures. When you allocate memory indirectly by calling a Toolbox routine, there is always a corresponding Toolbox routine to release that memory. For example, the DisposeWindow procedure releases memory allocated with the NewWindow function. Be sure to use these special Toolbox routines instead of the generic Memory Manager routines when applicable.